/**
 *  Copyright (C) 2021 Anthony Chomienne
 *  This program is free software: you can redistribute it and/or modify it under the terms of the
 *  GNU Affero General Public License as published by the Free Software Foundation, version 3.
 *
 *  This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
 *  without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 *  See the GNU Affero General Public License for more details.
 *
 *  You should have received a copy of the GNU Affero General Public License along with this program.
 *  If not, see <https://www.gnu.org/licenses/>
 */

package fr.mobdev.peertubelive.activity

import android.Manifest.permission
import android.content.*
import android.content.pm.PackageManager
import android.hardware.SensorManager
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.provider.Settings
import android.view.OrientationEventListener
import android.view.SurfaceHolder
import android.view.View
import android.view.WindowManager
import android.widget.Toast
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import androidx.databinding.DataBindingUtil
import com.pedro.encoder.input.video.CameraHelper
import com.pedro.rtplibrary.rtmp.RtmpCamera2
import net.ossrs.rtmp.ConnectCheckerRtmp
import fr.mobdev.peertubelive.R
import fr.mobdev.peertubelive.databinding.StreamBinding
import fr.mobdev.peertubelive.manager.InstanceManager.EXTRA_DATA
import fr.mobdev.peertubelive.objects.StreamData

class StreamActivity : AppCompatActivity() {


    private lateinit var binding: StreamBinding
    private lateinit var rtmpCamera2 : RtmpCamera2
    private lateinit var streamData: StreamData
    private lateinit var orientationEventListener: OrientationEventListener
    private lateinit var lockReceiver: BroadcastReceiver
    private var surfaceInit: Boolean = false
    private var permissionGiven: Boolean = false
    private var streamIsActive: Boolean = false
    private var screenOrientation: Int = 0
    private var lastScreenOrientation: Int = 0
    private var orientationCounter: Int = 0
    private var rotationIsLanternEnabled: Boolean = true

    companion object {
        const val BACKGROUND :Int = 1
        const val LOCK :Int = 2
        const val BACK :Int = 3
        const val STOP :Int = 4
        const val NETWORK_ERROR :Int = 5

    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
        binding = DataBindingUtil.setContentView(this, R.layout.stream)

        orientationEventListener = object: OrientationEventListener(this, SensorManager.SENSOR_DELAY_NORMAL){
            override fun onOrientationChanged(orientation: Int) {
                if(orientation < 0 || !rotationIsLanternEnabled)
                    return
                var localOrientation: Int
                localOrientation = when (orientation) {
                    in 45..135 -> {
                        90
                    }
                    in 135..225 -> {
                        180
                    }
                    in 225..315 -> {
                        270
                    }
                    else -> {
                        0
                    }
                }

                if(localOrientation != lastScreenOrientation) {
                    lastScreenOrientation = localOrientation
                    orientationCounter = 0
                } else {
                    orientationCounter++
                }

                if (lastScreenOrientation != screenOrientation && orientationCounter > 30) {
                    screenOrientation = lastScreenOrientation
                    rtmpCamera2.glInterface.setStreamRotation(screenOrientation)

                    if (screenOrientation == 90) {
                        localOrientation = 270
                    } else if(screenOrientation == 270) {
                        localOrientation = 90
                    }
                    binding.flash.rotation = localOrientation.toFloat()
                    binding.muteMicro.rotation = localOrientation.toFloat()
                    binding.switchCamera.rotation = localOrientation.toFloat()
                }
            }
        }
        orientationEventListener.enable()

        val parcelableExtra = intent.getParcelableExtra<StreamData>(EXTRA_DATA)
        streamData = parcelableExtra ?: StreamData("rtmp://47.111.80.242/myapp", "mystream"  ,StreamData.STREAM_RESOLUTION.p480)

        binding.stop.setOnClickListener {
            askStopLive(STOP)
        }
        binding.switchCamera.setOnClickListener { rtmpCamera2.switchCamera()  }
        binding.switchCamera.visibility = View.GONE
        binding.muteMicro.setOnClickListener {
            if (rtmpCamera2.isAudioMuted) {
                rtmpCamera2.enableAudio()
                binding.muteMicro.setImageResource(R.drawable.baseline_volume_up_24)
            }
            else {
                rtmpCamera2.disableAudio()
                binding.muteMicro.setImageResource(R.drawable.baseline_volume_off_24)
            }
        }
        binding.muteMicro.visibility = View.GONE
        binding.flash.setOnClickListener {
            if (rtmpCamera2.isLanternEnabled) {
                rtmpCamera2.disableLantern()
                binding.flash.setImageResource(R.drawable.baseline_flash_off_24)
            }
            else {
                rtmpCamera2.enableLantern()
                binding.flash.setImageResource(R.drawable.baseline_flash_on_24)
            }
        }
        binding.flash.visibility = View.GONE

        binding.rotation.setOnClickListener {
            if (rotationIsLanternEnabled) {
                rotationIsLanternEnabled = !rotationIsLanternEnabled
                binding.rotation.setImageResource(R.drawable.baseline_screen_lock_rotation_24)
            }
            else {
                rotationIsLanternEnabled = !rotationIsLanternEnabled
                binding.rotation.setImageResource(R.drawable.baseline_screen_rotation_24)
            }
        }
        binding.rotation.visibility = View.GONE
        binding.surfaceView.holder.addCallback(object: SurfaceHolder.Callback {
            override fun surfaceCreated(p0: SurfaceHolder) {
                surfaceInit = true
                if (permissionGiven)
                    startStream()
            }

            override fun surfaceChanged(p0: SurfaceHolder, p1: Int, p2: Int, p3: Int) {

            }

            override fun surfaceDestroyed(p0: SurfaceHolder) {
                if (this@StreamActivity::rtmpCamera2.isInitialized) {
                    if (rtmpCamera2.isStreaming) {
                        rtmpCamera2.stopStream()
                    }
                    rtmpCamera2.stopPreview()
                }

            }

        }
        )

        lockReceiver = object : BroadcastReceiver() {
            override fun onReceive(context: Context?, intent: Intent?) {
                if (intent?.action.equals(Intent.ACTION_SCREEN_OFF)){
                    setResult(LOCK)
                    finish()
                } else if (intent?.action.equals(Intent.ACTION_CLOSE_SYSTEM_DIALOGS)){
                    val reason = intent?.getStringExtra("reason")
                    if(reason.equals("homekey")){
                        setResult(BACKGROUND)
                        finish()
                    }
                }
            }
        }
        val filter = IntentFilter(Intent.ACTION_SCREEN_OFF)
        filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS)
        registerReceiver(lockReceiver, filter)
    }

    override fun onResume() {
        super.onResume()
        val permissions = getUnAllowedPermissions()

        if(permissions.isNotEmpty()) {
            var shouldShowRequest = true
            for(perm in permissions){
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
                    shouldShowRequest = shouldShowRequest && shouldShowRequestPermissionRationale(perm)
            }

            if(shouldShowRequest) {
                binding.permissionInfo.visibility = View.VISIBLE
                binding.gotoPermission.visibility = View.VISIBLE
                binding.surfaceView.visibility = View.GONE

                binding.gotoPermission.setOnClickListener {
                    val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
                    intent.data = Uri.fromParts("package", packageName, null)
                    startActivity(intent)
                }
            } else {
                ActivityCompat.requestPermissions(this, permissions.toTypedArray(), 1)
            }
        } else {
            binding.surfaceView.visibility = View.VISIBLE
            permissionGiven = true
            if(surfaceInit && !streamIsActive)
                startStream()
        }
    }

    override fun onStop() {
        super.onStop()
        if (this@StreamActivity::rtmpCamera2.isInitialized && !hasWindowFocus()) {
            unregisterReceiver(lockReceiver)
            setResult(BACKGROUND)
            finish()
        }
    }

    override fun onBackPressed() {
        askStopLive(BACK)
    }

    override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults)
        var allPermissionGranted = true
        for (result in grantResults) {
            allPermissionGranted = allPermissionGranted && (result == PackageManager.PERMISSION_GRANTED)
        }

        if (allPermissionGranted) {
            permissionGiven = true
            if(surfaceInit && !streamIsActive)
                startStream()
        } else  {
            binding.permissionInfo.visibility = View.VISIBLE
            binding.gotoPermission.visibility = View.VISIBLE
            binding.surfaceView.visibility = View.GONE
        }
    }

    private fun startStream() {
        streamIsActive = true
        binding.permissionInfo.visibility = View.GONE
        binding.gotoPermission.visibility = View.GONE
        binding.surfaceView.visibility = View.VISIBLE
        binding.muteMicro.visibility = View.VISIBLE
        binding.rotation.visibility = View.VISIBLE
        binding.switchCamera.visibility = View.VISIBLE
        binding.flash.visibility = View.VISIBLE

        val connectChecker : ConnectCheckerRtmp = object : ConnectCheckerRtmp {
            override fun onConnectionSuccessRtmp() {
                runOnUiThread {
                    Toast.makeText(binding.root.context, "Connection success", Toast.LENGTH_SHORT).show();

                }
            }

            override fun onConnectionFailedRtmp(reason: String) {
                runOnUiThread {
                    Toast.makeText(binding.root.context, "Connection failed", Toast.LENGTH_SHORT).show();
                    rtmpCamera2.stopStream()
                    setResult(NETWORK_ERROR)
                    finish()
                }
            }

            override fun onNewBitrateRtmp(bitrate: Long) {

            }

            override fun onDisconnectRtmp() {
                runOnUiThread {
                    Toast.makeText(binding.root.context, "Disconnect", Toast.LENGTH_SHORT).show();
                }

            }

            override fun onAuthErrorRtmp() {
                runOnUiThread {
                    Toast.makeText(binding.root.context, "Auth Error", Toast.LENGTH_SHORT).show();
                }

            }

            override fun onAuthSuccessRtmp() {
                runOnUiThread {
                    Toast.makeText(binding.root.context, "Auth Success", Toast.LENGTH_SHORT).show();
                }
            }
        }

        rtmpCamera2 = RtmpCamera2(binding.surfaceView, connectChecker)

        var width = 1920
        var height = 1080
        when (streamData.resolution) {
            StreamData.STREAM_RESOLUTION.p2160 -> {
                width = 3840
                height = 2160
            }
            StreamData.STREAM_RESOLUTION.p1440 -> {
                width = 2560
                height = 1440
            }
            StreamData.STREAM_RESOLUTION.p1080 -> {
                width = 1920
                height = 1080
            }
            StreamData.STREAM_RESOLUTION.p720 -> {
                width = 1280
                height = 720
            }
            StreamData.STREAM_RESOLUTION.p480 -> {
                width = 640
                height = 480
            }
            StreamData.STREAM_RESOLUTION.p360 -> {
                width = 480
                height = 360
            }
        }

        rtmpCamera2.startPreview(CameraHelper.Facing.BACK, width,height)

//start stream

        if (rtmpCamera2.prepareAudio() && rtmpCamera2.prepareVideo(width,height,30, (width*height*30*0.076).toInt(),false,CameraHelper.getCameraOrientation(this))) {
            rtmpCamera2.startStream(streamData.url+"/"+streamData.key)
        } else {
            /**This device cant init encoders, this could be for 2 reasons: The encoder selected doesnt support any configuration setted or your device hasnt a H264 or AAC encoder (in this case you can see log error valid encoder not found) */
        }
    }

    private fun getUnAllowedPermissions(): List<String> {
        val permissions: ArrayList<String> = ArrayList<String>()

        if (ContextCompat.checkSelfPermission(this, permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
            permissions.add(permission.CAMERA)
        }
        if (ContextCompat.checkSelfPermission(this, permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED) {
            permissions.add(permission.RECORD_AUDIO)
        }
        return permissions
    }

    private fun askStopLive(reason: Int) {
        val alertBuilder = AlertDialog.Builder(this)
        alertBuilder.setTitle(R.string.end_stream)
        alertBuilder.setMessage(R.string.ask_end_stream)
        alertBuilder.setPositiveButton(R.string.yes) { _: DialogInterface, _: Int ->
            setResult(reason)
            finish()
        }
        alertBuilder.setNegativeButton(R.string.no,null)
        alertBuilder.show()
    }

}